const isObject = (value) => {
  return value && Object.prototype.toString.call(value) === '[object Object]';
};

const isArray = (value) => {
  return value && Object.prototype.toString.call(value) === '[object Array]';
};

const isNumber = (value) => {
  return typeof value === 'number' && !isNaN(value);
};

const toFixed = (value, num = 2) => {
  return value.toFixed(num) / 1;
};

// 获取对象类型
const getType = (obj) => {
  const toString = Object.prototype.toString;
  const map = {
    '[object Boolean]': 'boolean', // 布尔
    '[object Number]': 'number', // 数字
    '[object String]': 'string', // 字符串
    '[object Function]': 'function', // 方法
    '[object Array]': 'array', // 数组
    '[object Date]': 'date', // 日期
    '[object RegExp]': 'regExp', // 正则
    '[object Undefined]': 'undefined', // undefined
    '[object Null]': 'null', // null
    '[object Object]': 'object', // 对象
  };
  if (obj instanceof Element) {
    return 'element'; // dom
  }
  return map[toString.call(obj)];
}


//深层合并对象
const mergeDeep = (oldValue, newValue) => {
  if (oldValue && newValue) {
    if (isObject(oldValue) && isObject(newValue)) {
      Object.keys(newValue).forEach((key) => {
        const newData = newValue[key];
        const oldData = oldValue[key];
        if (isObject(newData)) {
          oldValue[key] = mergeDeep(oldData || {}, newData);
        } else if (isArray(newData) && key === 'series') {
          // series特判
          oldValue[key] = mergeDeep(oldData || [], newData);
        } else if (isArray(newData) && key === 'data') {
          // data特判
          oldValue[key] = newData;
        } else {
          oldValue[key] = newData;
        }
      });
    } else if (isArray(oldValue) && isArray(newValue)) {
      newValue.forEach((v, i) => {
        oldValue[i] =
          isObject(oldValue[i]) || isObject(newValue[i])
            ? mergeDeep(oldValue[i] || {}, newValue[i] || {})
            : newValue[i] || oldValue[i];
      });
    }
  } else if (newValue) {
    oldValue = newValue;
  }
  return oldValue;
};

 const hasClass = (el, cls) => {
   if (!el || !cls) return false;
   if (cls.indexOf(' ') !== -1) throw new Error('className should not contain space.');
   if (el.classList) {
     return el.classList.contains(cls);
   } else {
     return (' ' + el.className + ' ').indexOf(' ' + cls + ' ') > -1;
   }
 };
 const addClass = (el, cls) => {
   if (!el) return;
   let curClass = el.className;
   let classes = (cls || '').split(' ');

   for (let i = 0, j = classes.length; i < j; i++) {
     let clsName = classes[i];
     if (!clsName) continue;

     if (el.classList) {
       el.classList.add(clsName);
     } else if (!hasClass(el, clsName)) {
       curClass += ' ' + clsName;
     }
   }
   if (!el.classList) {
     el.setAttribute('class', curClass);
   }
 };
 const removeClass = (el, cls) => {
   if (!el || !cls) return;
   let classes = cls.split(' ');
   let curClass = ' ' + el.className + ' ';

   for (let i = 0, j = classes.length; i < j; i++) {
     let clsName = classes[i];
     if (!clsName) continue;

     if (el.classList) {
       el.classList.remove(clsName);
     } else if (hasClass(el, clsName)) {
       curClass = curClass.replace(' ' + clsName + ' ', ' ');
     }
   }
   if (!el.classList) {
     el.setAttribute('class', trim(curClass));
   }
 };
 const trim = (string) => {
   return (string || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '');
 };
 const formatProps = (oldValue, newValue) => {
   return { ...oldValue, ...newValue };
 };

 const on = (element, event, handler) => {
   if (element && event && handler) {
     document.addEventListener
       ? element.addEventListener(event, handler, false)
       : element.attachEvent('on' + event, handler);
   }
 };

 const off = (element, event, handler) => {
   if (element && event) {
     document.removeEventListener
       ? element.removeEventListener(event, handler, false)
       : element.detachEvent('on' + event, handler);
   }
 };

 const elementContains = (elm, otherElm) => {
   if (typeof elm.contains === 'function') {
     return elm.contains(otherElm);
   }
   return false;
 };

export {
  isObject,
  isArray,
  isNumber,
  toFixed,
  mergeDeep,
  getType,
  hasClass,
  addClass,
  removeClass,
  trim,
  formatProps,
  on,
  off,
  elementContains,
};
